import random
import string
from logging import getLogger
from typing import Optional

from tortoise.transactions import atomic

from app.core.constants import PointSourceType
from app.core.exceptions import APIException
from app.core.i18n import t
from app.models.rbac_model import ButtonResourceModel, MenuResourceModel, UserRoleModel
from app.models.user_business_model import UserBalanceRecordModel
from app.models.user_model import UserModel, UserProfileModel
from app.schemas.auth_schemas import UserInfoResponse
from app.schemas.common import OrderSchema, PaginationParams, PaginationResult
from app.schemas.user import (
    BalanceRechargeRequest,
    PasswordChangeRequest,
    PointsRechargeRequest,
    UserCreateRequest,
    UserCreateResponse,
    UserDeleteResponse,
    UserDetailResponse,
    UserFilters,
    UserUpdateRequest,
    UserUpdateResponse,
)

from ..base import filter_query

logger = getLogger(__name__)


def random_password() -> str:
    """生成随机密码"""
    return "".join(random.choices(string.ascii_letters + string.digits, k=10))


class AdminUserManageService:
    @classmethod
    @atomic()
    async def create(cls, data: UserCreateRequest) -> UserCreateResponse:
        """Create user with profile in a transaction"""

        # Check username existence
        if await UserModel.filter(username=data.username).exists():
            raise APIException(message=t("error.user.username_exists"))

        # Check email if provided
        if data.email and await UserModel.filter(email=data.email).exists():
            raise APIException(message=t("error.user.email_exists"))

        # 提出 profile
        data_dict = data.model_dump(exclude_unset=True)
        data_dict.pop("profile")

        # 提供随机密码
        data_dict["password"] = UserModel.encryption_password(random_password())

        # Create user
        user = await UserModel.create(**data_dict)

        # Create profile if provided
        if data.profile:
            await UserProfileModel.create(user=user, **data.profile.model_dump(exclude_unset=True))

        # 返回用户情
        user = await UserModel.get_or_none(id=user.id).select_related("profile")

        return UserCreateResponse.model_validate(user)

    @classmethod
    async def get_by_id(cls, user_id: int) -> Optional[UserDetailResponse]:
        """根据ID获取用户"""
        user = await UserModel.get_or_none(id=user_id).select_related("profile")
        if not user:
            raise APIException(message=t("error.user.not_found"))
        return UserDetailResponse.model_validate(user)

    @classmethod
    @atomic()
    async def update(cls, user_id: int, data: UserUpdateRequest) -> UserUpdateResponse:
        """Update user and profile"""
        user = await UserModel.get_or_none(id=user_id).select_related("profile")
        if not user:
            raise APIException(message=t("error.user.not_found"))

        # Check username uniqueness
        if data.username and data.username != user.username:
            if await UserModel.filter(username=data.username).exists():
                raise APIException(message=t("error.user.username_exists"))

        # Check email uniqueness
        if data.email and data.email != user.email:
            if await UserModel.filter(email=data.email).exists():
                raise APIException(message=t("error.user.email_exists"))

        # Check phone uniqueness
        if data.phone and data.phone != user.phone:
            if await UserModel.filter(phone=data.phone).exists():
                raise APIException(message=t("error.user.phone_exists"))

        # Extract and update profile data
        data_dict = data.model_dump(exclude_unset=True)
        profile_data = data_dict.pop("profile", None)

        # Update user data
        await user.update_from_dict(data_dict).save()

        # Update profile if provided
        if profile_data and user.profile:
            await user.profile.update_from_dict(profile_data).save()
        elif profile_data:
            await user.profile.create(**profile_data)

        # 修改后的
        return UserUpdateResponse.model_validate(user)

    @classmethod
    @atomic()
    async def delete(cls, user_id: int) -> UserDeleteResponse:
        """删除用户"""
        user = await UserModel.get_or_none(id=user_id).select_related("profile")
        if not user:
            raise APIException(message=t("error.user.not_found"))
        await user.profile.delete()
        await user.delete()

        return UserDeleteResponse.model_validate(user)

    @classmethod
    async def list(
        cls, filters: UserFilters, order: OrderSchema, pagination: PaginationParams
    ) -> PaginationResult[UserDetailResponse]:
        """获取用户列表"""
        special_filter_conditions = {
            "username": "username__icontains",
            "email": "email__icontains",
            "phone": "phone__icontains",
        }
        query = UserModel.all().select_related("profile")
        query = await filter_query(query, filters, special_filter_conditions)

        if order and order.order_by:
            query = query.order_by(*order.get_tortoise_orders())

        total = await query.count()
        items = await query.offset(pagination.get_skip()).limit(pagination.page_size).all()

        return PaginationResult(total=total, items=items)

    @classmethod
    @atomic()
    async def change_password(cls, user_id: int, data: PasswordChangeRequest) -> UserDetailResponse:
        """Change user password"""
        user = await UserModel.get_or_none(id=user_id)
        if not user:
            raise APIException(message=t("error.user.not_found"))

        if data.new_password != data.re_password:
            raise APIException(message=t("error.user.password_not_match"))

        # Verify old password
        if not user.verify_password(data.old_password):
            raise APIException(message=t("error.user.invalid_password"))

        # Update password
        user.password = UserModel.encryption_password(data.new_password)
        await user.save()

        return UserDetailResponse.model_validate(user)

    @classmethod
    @atomic()
    async def recharge_balance(cls, user_id: int, data: BalanceRechargeRequest) -> UserDetailResponse:
        """Recharge user balance"""
        user = await UserModel.get_or_none(id=user_id).select_related("profile")
        if not user:
            raise APIException(message=t("error.user.not_found"))

        if not user.profile:
            logger.error(f"User profile not found for user {user_id}")
            raise APIException(message=t("error.user.profile_not_found"))

        if data.amount <= 0:
            raise APIException(message=t("error.user.invalid_amount"))

        # 创建余额记录
        await UserBalanceRecordModel.create(
            user=user,
            amount=data.amount,
            before_balance=user.balance,
            after_balance=user.balance + data.amount,
            source_type=PointSourceType.ADMIN,
            remark=f"余额充值",
        )

        user.balance += data.amount
        await user.save()

        return UserDetailResponse.model_validate(user)

    @classmethod
    @atomic()
    async def recharge_points(cls, user_id: int, data: PointsRechargeRequest) -> UserDetailResponse:
        """Recharge user points"""
        user = await UserModel.get_or_none(id=user_id).select_related("profile")
        if not user:
            raise APIException(message=t("error.user.not_found"))

        if not user.profile:
            logger.error(f"User profile not found for user {user_id}")
            raise APIException(message=t("error.user.profile_not_found"))

        if data.points <= 0:
            raise APIException(message=t("error.user.invalid_points"))

        # 创建积分记录
        await UserBalanceRecordModel.create(
            user=user,
            amount=data.points,
            before_balance=user.balance,
            after_balance=user.balance + data.points,
            source_type=PointSourceType.ADMIN,
            remark=f"积分充值",
        )

        user.points += data.points
        await user.save()

        return UserDetailResponse.model_validate(user)


class AdminUserInfoService:
    @classmethod
    async def get_user_info(cls, user_id: int) -> UserInfoResponse:
        """获取用户信息

        class UserInfoResponse(BaseModel):
            user_id: int = Field(..., description="用户ID")
            nickname: str = Field(..., description="用户名")
            avatar: str = Field(..., description="头像")
            menus: list[dict] = Field(..., description="菜单")
            permissions: list[str] = Field(..., description="权限")
        """
        user = await UserModel.get_or_none(id=user_id).prefetch_related("roles")
        if not user:
            raise APIException(message=t("error.user.not_found"))

        # 获取全部角色，并获取角色对应的菜单和按钮
        roles_ids = [role.id for role in user.roles]

        # 获取用户菜单
        if roles_ids:
            user_menus = await MenuResourceModel.filter(roles__in=roles_ids).all()
        else:
            user_menus = []
        user_menus_dict = {menu.id: menu.to_dict() for menu in user_menus}
        # to_tree

        # 获取用户权限
        if roles_ids:
            user_permissions = await ButtonResourceModel.filter(roles__in=roles_ids).values("code")
        else:
            user_permissions = []
        profile = await UserProfileModel.get_or_none(user_id=user.id)
        return UserInfoResponse.model_validate(
            {
                "user_id": user.id,
                "nickname": profile.nickname if profile else "",
                "avatar": profile.avatar if profile else "",
                "menus": user_menus_dict,
                "permissions": user_permissions,
            }
        )
